﻿/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#include "regular_mesh_2d.h"

void gctl::regular_mesh_2d::init(std::string in_name, std::string in_info, int xbnum, int ybnum, 
	double xmin, double ymin, double xsize, double ysize)
{
	if (initialized)
	{
		std::string err_str = "The object is already initialized. From void regular_mesh_2d::init(...)";
		throw runtime_error(err_str);
	}

	base_mesh::init(REGULAR_MESH, MESH_2D, in_name, in_info);

	rm_xbnum = xbnum; rm_ybnum = ybnum;
	rm_xmin = xmin; rm_ymin = ymin;
	rm_xsize = xsize; rm_ysize = ysize;

	node_num = (rm_xbnum+1) * (rm_ybnum+1);
	ele_num  = rm_xbnum * rm_ybnum;

	nodes.resize(node_num);
	elements.resize(ele_num);

	for (int j = 0; j < rm_ybnum+1; j++)
	{
		for (int i = 0; i < rm_xbnum+1; i++)
		{
			nodes[i+j*(rm_xbnum+1)].id = i+j*(rm_xbnum+1);
			nodes[i+j*(rm_xbnum+1)].x  = rm_xmin+(i-0.5)*rm_xsize;
			nodes[i+j*(rm_xbnum+1)].y  = rm_ymin+(j-0.5)*rm_ysize;
		}
	}

	for (int j = 0; j < rm_ybnum; j++)
	{
		for (int i = 0; i < rm_xbnum; i++)
		{
			elements[i+j*rm_xbnum].id = i+j*rm_xbnum;
			elements[i+j*rm_xbnum].vert[0] = nodes.get(i+j*(rm_xbnum+1));
			elements[i+j*rm_xbnum].vert[1] = nodes.get(i+1+j*(rm_xbnum+1));
			elements[i+j*rm_xbnum].vert[2] = nodes.get(i+1+(j+1)*(rm_xbnum+1));
			elements[i+j*rm_xbnum].vert[3] = nodes.get(i+(j+1)*(rm_xbnum+1));
			elements[i+j*rm_xbnum].dl = nodes.get(i+j*(rm_xbnum+1));
			elements[i+j*rm_xbnum].ur = nodes.get(i+1+(j+1)*(rm_xbnum+1));
		}
	}

	initialized = true;
	return;
}

void gctl::regular_mesh_2d::load_binary(std::string filename)
{
	if (initialized)
	{
		std::string err_str = "The object is already initialized. From void regular_mesh_2d::load_binary(...)";
		throw runtime_error(err_str);
	}

	std::ifstream infile;
	gctl::open_infile(infile, filename, ".2m", 
		std::ios::in|std::ios::binary);

	// 读入网格头信息
	load_headinfo(infile, REGULAR_MESH, MESH_2D);

	// 读入网格信息
	infile.read((char*)&rm_xbnum, sizeof(int));
	infile.read((char*)&rm_ybnum, sizeof(int));
	infile.read((char*)&rm_xmin, sizeof(double));
	infile.read((char*)&rm_ymin, sizeof(double));
	infile.read((char*)&rm_xsize, sizeof(double));
	infile.read((char*)&rm_ysize, sizeof(double));

	node_num = (rm_xbnum+1) * (rm_ybnum+1);
	ele_num  = rm_xbnum * rm_ybnum;

	nodes.resize(node_num);
	elements.resize(ele_num);

	for (int j = 0; j < rm_ybnum+1; j++)
	{
		for (int i = 0; i < rm_xbnum+1; i++)
		{
			nodes[i+j*(rm_xbnum+1)].id = i+j*(rm_xbnum+1);
			nodes[i+j*(rm_xbnum+1)].x  = rm_xmin+(i-0.5)*rm_xsize;
			nodes[i+j*(rm_xbnum+1)].y  = rm_ymin+(j-0.5)*rm_ysize;
		}
	}

	for (int j = 0; j < rm_ybnum; j++)
	{
		for (int i = 0; i < rm_xbnum; i++)
		{
			elements[i+j*rm_xbnum].id = i+j*rm_xbnum;
			elements[i+j*rm_xbnum].vert[0] = nodes.get(i+j*(rm_xbnum+1));
			elements[i+j*rm_xbnum].vert[1] = nodes.get(i+1+j*(rm_xbnum+1));
			elements[i+j*rm_xbnum].vert[2] = nodes.get(i+1+(j+1)*(rm_xbnum+1));
			elements[i+j*rm_xbnum].vert[3] = nodes.get(i+(j+1)*(rm_xbnum+1));
			elements[i+j*rm_xbnum].dl = nodes.get(i+j*(rm_xbnum+1));
			elements[i+j*rm_xbnum].ur = nodes.get(i+1+(j+1)*(rm_xbnum+1));
		}
	}

	initialized = true;

	// 读入模型数据单元
	load_datablock(infile);

	infile.close();
	return;
}

void gctl::regular_mesh_2d::save_binary(std::string filename)
{
	if (!initialized)
	{
		std::string err_str = "The object is not initialized. From void regular_mesh_2d::save_binary(...)";
		throw runtime_error(err_str);
	}

	std::ofstream outfile;
	gctl::open_outfile(outfile, filename, ".2m", 
		std::ios::out|std::ios::binary);

	// 首先输出网格的头信息
	save_headinfo(outfile);

	// 输出网格信息
	outfile.write((char*)&rm_xbnum, sizeof(int));
	outfile.write((char*)&rm_ybnum, sizeof(int));
	outfile.write((char*)&rm_xmin, sizeof(double));
	outfile.write((char*)&rm_ymin, sizeof(double));
	outfile.write((char*)&rm_xsize, sizeof(double));
	outfile.write((char*)&rm_ysize, sizeof(double));

	// 输出的模型数据单元
	save_datablock(outfile);

	outfile.close();
	return;
}

void *gctl::regular_mesh_2d::get_node(unsigned int idx)
{
	std::string err_str;
	if (!initialized)
	{
		err_str = "The object is not initialized. From gctl::regular_mesh_2d::get_node(...)";
		throw runtime_error(err_str);
	}

	if (idx >= node_num)
	{
		err_str = "Invalid index. From gctl::regular_mesh_2d::get_node(...)";
		throw runtime_error(err_str);
	}

	return nodes.get(idx);
}

void *gctl::regular_mesh_2d::get_element(unsigned int idx)
{
	std::string err_str;
	if (!initialized)
	{
		err_str = "The object is not initialized. From gctl::regular_mesh_2d::get_element(...)";
		throw runtime_error(err_str);
	}

	if (idx >= ele_num)
	{
		err_str = "Invalid index. From gctl::regular_mesh_2d::get_element(...)";
		throw runtime_error(err_str);
	}

	return elements.get(idx);
}

gctl::regular_mesh_2d::regular_mesh_2d() : base_mesh::base_mesh(){}

gctl::regular_mesh_2d::regular_mesh_2d(std::string in_name, std::string in_info, int xbnum, int ybnum, 
	double xmin, double ymin, double xsize, double ysize)
{
	init(in_name, in_info, xbnum, ybnum, xmin, ymin, xsize, ysize);
}

gctl::regular_mesh_2d::~regular_mesh_2d(){}

int gctl::regular_mesh_2d::get_xbnum() const
{
	if (!initialized)
	{
		std::string err_str = "The object is not initialized. From void regular_mesh_2d::get_xbnum(...)";
		throw runtime_error(err_str);
	}

	return rm_xbnum;
}

int gctl::regular_mesh_2d::get_ybnum() const
{
	if (!initialized)
	{
		std::string err_str = "The object is not initialized. From void regular_mesh_2d::get_ybnum(...)";
		throw runtime_error(err_str);
	}

	return rm_ybnum;
}

double gctl::regular_mesh_2d::get_xmin() const
{
	if (!initialized)
	{
		std::string err_str = "The object is not initialized. From void regular_mesh_2d::get_xmin(...)";
		throw runtime_error(err_str);
	}

	return rm_xmin;
}

double gctl::regular_mesh_2d::get_ymin() const
{
	if (!initialized)
	{
		std::string err_str = "The object is not initialized. From void regular_mesh_2d::get_ymin(...)";
		throw runtime_error(err_str);
	}

	return rm_ymin;
}

double gctl::regular_mesh_2d::get_xsize() const
{
	if (!initialized)
	{
		std::string err_str = "The object is not initialized. From void regular_mesh_2d::get_xsize(...)";
		throw runtime_error(err_str);
	}

	return rm_xsize;
}

double gctl::regular_mesh_2d::get_ysize() const
{
	if (!initialized)
	{
		std::string err_str = "The object is not initialized. From void regular_mesh_2d::get_ysize(...)";
		throw runtime_error(err_str);
	}

	return rm_ysize;
}

void gctl::regular_mesh_2d::save_gmsh(std::string filename, index_packed_e packed)
{
	std::ofstream outfile;
	gctl::open_outfile(outfile, filename, ".msh");
	gctl::save2gmsh(outfile, elements, nodes, packed);
	outfile.close();
	return;
}

void gctl::regular_mesh_2d::save_gmsh(std::string filename, mesh_data_type_e d_type, output_type_e out_mode, index_packed_e packed)
{
	base_mesh::save_gmsh(filename, d_type, out_mode, packed);
	return;
}

void gctl::regular_mesh_2d::save_gmsh(std::string filename, std::string datname, output_type_e out_mode, index_packed_e packed)
{
	base_mesh::save_gmsh(filename, datname, out_mode, packed);
	return;
}
